﻿using System;
using System.Collections.Generic;
using System.Linq;
using System.Text;
using System.Text.RegularExpressions;

namespace Tressel_asm
{
    class Operands : InstructionComponentBase
    {
        public Operands(string operandStringParam, Instruction instructionReference, string operandsReq)
        {
            /*!
Procedure Name: Operands
Description: The constructor for the Operands class.  Now takes the entire operands section, not just one operand
Input Parameters: string operandStringParam (the text itself), instructionReference (used to call back to the instruction and/or the program), operandsReq (the Regex filter or keyword specifying the operand format)
Output Parameters: none
Error Conditions Tested:
            Tested/checked syntax of all operands aside from a select few (EQU, EQU-exp, ADR-Comp)
Error Messages Generated:
            (See Error message table for descriptive messages)
            [10, 11, 12, 13, 19, 26, 27, 34, 38, 39, 40, 41, 42, 43, 50, 51, 52, 53, 55]
Original Author: Steve Rudy
Procedure Creation Date: 10/7/2009
Modification Log:
Who: Steve Rudy
When: 10/10/2009
Why: Because this class was designed, like the others which composed an instruction, to have a 
similar parent, the operandString variable and its corresponding property have been removed now 
that the text property has been implemented on the parent class.
Who: Steve Rudy
When: 10/15/2009
Why: Originally, this class was supposed to handle one operand.  Now it has the function of 
handling the set of operands.  So the class was renamed, and much of its functionality altered.
Instead of simply making sure the one operand is a valid label/number, etc, now we use a mix of
C# and Regex to validate the entire string of operands.
Who: Greg Loesch
When: 10/16/2009
Why: Added more regex expressions and added/deleted some special cases from the switch statement. Other minor changes.
Who: Greg Loesch
When: 10/18/2009
Why: Added DED information to variables and removed some extraneous comments. 
Added a few cases to handle bad operands.
Who: Greg Loesch
When: 10/20/2009
Why: Added special case for Ref-All operands (had to handle literals). Also added DED info.
Who: Greg Loesch and Shawn Hall
When: 10/21/2009
Why: Edited and simplified the logic behind Ref-All.  Switched from RegEx to hybrid.
Who: Shawn Hall
When: 10/21/2009
Why: Changed handling in Ref-All to handle certain operands.
Who: Louis Pan
When: 10/30/2009
Why: operands shouldn't insert literal.
Who: Louis Pan
When: 11/1/2009
Why: 4Labels has to check basic requirement for labels.
Who: Greg Loesch, Ryan McDaniel
When: 11/1/2009
Why: 4Labels issue was fixed (when improper label appeared).
Who: Louis Pan
When: 11/4/2009
Why: error fixed for empty Text when it should not in 4Label check.
Who: Shawn, Greg
When: 11/4/2009
Why: Refall wouldnt accept #7 as valid
Who: Ryan
When: 11/8/2009
Why: Corrected max/min @BASE10 to -8388608<x<8388607
Who: Steve Rudy
When: 11/9/2009
Why: added more regex catching to make errors more specific and fixing handling of single quotes
Who: Steve Rudy
When: 11/10/2009
Why: Needed to change reg-ex expressions.
Who: Greg Loesch
When: 11/12/2009
Why: Needed to add a specific error message (LD-BR).
Who: Greg Loesch
When: 11/13/2009
Why: It needed fixed
Who: Ryan McDaniel
When: 11/14/2009
Why: Changed equated symbols to their respective substitution strings as soon as the operand enters the class.
Who: Greg Loesch
When: 11/15/2009
Why: Removed -'s from regex expressions
Coding Standards Met: SR
Testing Standards Met: RM
!*/

            text = operandStringParam;
            /*#
            Variable Name: myRow
            Module Where Defilned: Operands
            Data Type: DataRow []
            Local or Global: Local
            Purpose: store the rows for select label.
            #*/
            System.Data.DataRow[] myRow = instructionReference.TresselReference.SymbolTable.Select("[Label Name]='" + text.Replace("'", "''") + "'");
            if (myRow.Length > 0)
            {
                if (myRow[0]["Usage"].Equals("EQU"))
                {
                    text = myRow[0]["Substitution String"].ToString();
                }
            }
            if (operandsReq.StartsWith("@"))
            {
                operandsReq = operandsReq.Substring(1);
                switch (operandsReq)
                {
                    case "4LABELS":
                        {
                            /*#
                            Variable Name: comma
                            Module Where Defined: Operands
                            Data Type: Character
                            Local or Global: Local
                            Purpose: Created for use in Split method in the string class (character that will split a given string).
                            #*/
                            char comma = ',';

                            /*#
                            Variable Name: commaArray
                            Module Where Defined: Operands
                            Data Type: Character Array
                            Local or Global: Local
                            Purpose: Created for use in Split method in the string class (set of characters that will split a given string).
                            #*/
                            char[] commaArray = new char[1];
                            commaArray[0] = comma;

                            /*#
                           Variable Name: splitByComma
                           Module Where Defined: Operands
                           Data Type: String Array
                           Local or Global: Local
                           Purpose: Contains the portions of the string that was split. This holds potential labels.
                           #*/
                            string[] splitByCommas = text.Split(commaArray);
                            if (splitByCommas.Length < 5)
                            {
                                for (int i = 0; i < splitByCommas.Length; i++)
                                {
                                    if (!Regex.IsMatch(splitByCommas[i], @"^[a-zA-Z][a-zA-Z0-9\._]{1,63}$"))
                                    {
                                        this.ErrorFlag = true;
                                        splitByCommas[i] = "";
                                    }
                                }
                                text = "";
                                if(errorFlag)
                                {
                                    instructionReference.AddError(11);
                                }

                                for (int i = 0; i < splitByCommas.Length; i++)
                                {
                                    if (splitByCommas[0].ToString ().Length >= 2)
                                    {
                                        if (splitByCommas[i].ToString().Length > 0)
                                        {
                                            text += splitByCommas[i].ToString() + ",";
                                        }
                                    }
                                }
                                text = text.TrimEnd(commaArray);
                            }
                            else
                            {
                                text = "";
                                this.ErrorFlag = true;
                                instructionReference.AddError(10);
                            }
                            break;
                        }
                    case "BASE10":
                        {
                            /*#
                            Variable Name: number
                            Module Where Defined: Operands
                            Data Type: int
                            Local or Global: Local
                            Purpose: Holds the value of the base10 number. Initialized to 0.
                            #*/
                            int number = 0;
                            try
                            {
                                number = System.Convert.ToInt32(text);
                            }
                            catch
                            {
                                this.ErrorFlag = true;
                                instructionReference.AddError(12);
                                text = "0";
                            }

                            if (number < -8388608 || number > 8388607)
                            {
                                this.ErrorFlag = true;
                                instructionReference.AddError(11);
                                text = "0";
                            }
                            break;
                        }
                    case "REFALL":
                        {
                            /*#
                            Variable Name: isValid
                            Module Where Defined: Operands
                            Data Type: bool
                            Local or Global: Local
                            Purpose: Used as a flag to determine a valid Ref-All operand.
                            #*/
                            bool isValid = false;
                            /*#
                            Variable Name: temp
                            Module Where Defined: Operands
                            Data Type: string
                            Local or Global: Local
                            Purpose: Used to prevent modification of actual operand while validating it.
                            #*/
                            string temp = text;
                            if (!temp.Equals("*"))
                            {
                                if (temp.StartsWith("*+") || temp.StartsWith("*-"))
                                {
                                    temp = temp.Substring(2);
                                    if (Regex.IsMatch(temp, @"^([a-zA-Z][a-zA-Z0-9\._]{1,63}|(0*[0-1]?[0-9]?[0-9]?[0-9]|0*20[0-3][0-9]|0*204[0-7]))$"))
                                    {
                                        isValid = true;
                                    }
                                }
                                else if (temp.Length > 2)
                                {
                                    if (temp.Substring(temp.Length - 3).ToUpper().Equals(",IR"))
                                    {
                                        temp = temp.Substring(0, temp.Length - 3);
                                    }
                                    if (temp.StartsWith("#") || temp.StartsWith("%"))
                                    {
                                        temp = temp.Substring(1);
                                    }
                                    if (Regex.IsMatch(temp, @"^([a-zA-Z][a-zA-Z0-9\._]{1,63}|(0*[0-1]?[0-9]?[0-9]?[0-9]|0*20[0-3][0-9]|0*204[0-7]))$"))
                                    {
                                        isValid = true;
                                    }
                                }
                                else
                                {
                                    if (temp.StartsWith("#") || temp.StartsWith("%"))
                                    {
                                        temp = temp.Substring(1);
                                    }
                                    if (Regex.IsMatch(temp, @"^([a-zA-Z][a-zA-Z0-9\._]{1,63}|(0*[0-1]?[0-9]?[0-9]?[0-9]|0*20[0-3][0-9]|0*204[0-7]))$"))
                                    {
                                        isValid = true;
                                    }
                                }
                                if (!isValid)
                                {
                                    if (text.StartsWith("="))
                                    {
                                        /*#
                                        Variable Name: literalValue
                                        Module Where Defined: Operands
                                        Data Type: int
                                        Local or Global: Local
                                        Purpose: Holds the value of the literal (following '=')
                                        #*/
                                        int literalValue = 0;
                                        try
                                        {
                                            literalValue = Int32.Parse(text.Substring(1));
                                        }
                                        catch
                                        {
                                            if (!(text.Substring(1).StartsWith("'")&&text.Substring(1).EndsWith("'")) || text.Length >6)
                                            {
                                                this.errorFlag = true;
                                                instructionReference.AddError(26);
                                                text = "0";
                                            }
                                        }

                                        if (literalValue > 16777215)
                                        {
                                            this.errorFlag = true;
                                            instructionReference.AddError(26);
                                            text = "0";
                                        }
                                    }
                                    else
                                    {
                                        this.errorFlag = true;
                                        if (instructionReference.IOpcode.Text.Equals("EQU"))
                                        {
                                            instructionReference.AddError(53);
                                        }
                                        else
                                        {
                                            instructionReference.AddError(13);
                                        }
                                        text = "0";
                                    }
                                }
                              
                            }
                            break;
                        }
                    case "WORDCHR":
                        {
                            if (!(text.StartsWith("'") && text.EndsWith("'")))
                            {
                                this.errorFlag = true;
                                instructionReference.AddError(43);
                                text = "''";
                                instructionReference.IsValidInstruction = false;
                            }
                            else if (text.Length > 5)
                            {
                                this.errorFlag = true;
                                instructionReference.AddError(42);
                                instructionReference.IsValidInstruction = false;
                                text = "''";
                            }
                            break;
                        }
                    case "LD-BR": //Handles LD-BR case (since it's not longer a valid opcode
                        {
                            this.errorFlag = true;
                            instructionReference.AddError(55);
                            instructionReference.TresselReference.FatalErrors = true;
                            break;
                        }
                }
            }
            else
            {
                if (!Regex.IsMatch(text, operandsReq))
                {
                    //For more specific errors
                    switch (operandsReq.Trim())
                    {
                        case @"^[%#]?((0*[0-1]?[0-9]?[0-9]?[0-9]|0*20[0-3][0-9]|0*204[0-7])|([a-zA-Z][a-zA-Z0-9\._]{1,63}([,][Ii][Rr])?)|([*][+-](0*[0-1]?[0-9]?[0-9]?[0-9]|0*20[0-3][0-9]|0*204[0-7]))|([a-zA-Z][a-zA-Z0-9]{1,63}))$": //ref-all store instructions
                            {
                                text = "0";
                                errorFlag = true;
                                instructionReference.AddError(50);
                                break;
                            }
                        case "^(0*[0-1]?[0-9]?[0-9]?[0-9]|0*20[0-3][0-9]|0*204[0-7])$": // Type 1
                            {
                                text = "0";
                                this.ErrorFlag = true;
                                instructionReference.AddError(19);
                                break;
                            }
                        case "^(0*[0-9]|0*[0-1][0-9]|0*2[0-3])$": // Type 4
                            {
                                text = "0";
                                this.errorFlag = true;
                                instructionReference.AddError(41);
                                break;
                            }
                        case "^0*[0-2]$":   // Halt/Display
                            {
                                text = "0";
                                this.ErrorFlag = true;
                                instructionReference.AddError(34);
                                break;
                            }
                        case "^([A-Fa-f0-9]{1,6})$": // Word-hex
                            {
                                text = "0";
                                this.ErrorFlag = true;
                                instructionReference.AddError(38);
                                break;
                            }
                        case "^([0-1]{1,24})$": // Word-bin
                            {
                                text = "0";
                                this.errorFlag = true;
                                instructionReference.AddError(39);
                                break;
                            }
                        case "^([Oo][Nn]|[Oo][Ff][Ff])$": // debug
                            {
                                text = "off";
                                this.errorFlag = true;
                                instructionReference.AddError(40);
                                break;
                            }
                        case "^[*]$":   //LD-BR-ADR
                            {
                                instructionReference.AddError(27);
                                instructionReference.TresselReference.FatalErrors = true;
                                break;
                            }
                        case @"^((0*[0-1]?[0-9]?[0-9]?[0-9]|0*20[0-3][0-9]|0*204[0-7])|([a-zA-Z][a-zA-Z0-9\._]{1,63}))$":
                            {
                                text = "0";
                                this.ErrorFlag = true;
                                if (instructionReference.IOpcode.Text.Equals("ADR"))
                                {
                                    instructionReference.AddError(52); // ADR
                                }
                                else
                                {
                                    instructionReference.AddError(51); // Type 2 error
                                }
                                break;
                            }
                        default:   
                            {
                                text = "0";
                                this.ErrorFlag = true;
                                instructionReference.AddError(13);
                                break;
                            }
                    }
                }
            }
        }
        /*!
        Procedure Name: Operands (version 2)
        Description: is an empty constructor for Operands
        Input Parameters: none
        Output Parameters: none
        Error Conditions Tested:
        Error Messages Generated:
        Original Author: Steve Rudy
        Procedure Creation Date: 10/19/2009
        Modification Log:
        Coding Standards Met: SR
        Testing Standards Met: RM
        !*/
        public Operands()
        {
        }
    }
}
